package client;


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Date;
import java.util.MissingResourceException;

import message.Response;
import message.request.BuyRequest;
import message.request.CryptLoginRequest;
import message.request.DownloadTicketRequest;
import message.request.LoginRequest;
import message.request.UploadRequest;
import message.response.DownloadFileResponse;
import message.response.LoginResponse;
import message.response.MessageResponse;
import proxy.ProxyManagementComponent;
import util.Config;
import cli.Command;
import cli.Shell;

public class ClientCli implements IClientCli, IRMICallback {

	private Shell shell;
	private Thread shellThread;

	private Proxy proxy;

	private String downloadDirPath;
	private String proxyHost;
	private int proxyTcpPort;
	
	private File downloadDir;
	private Config messages;
	
	private String loggedInUserName = null;
	

	/**
	Constructor to Start a Client from the TestComponent (sets Properties)
	 */
	public ClientCli(Shell shell, String downloadDirPath, String proxyHost, int proxyTcpPort) {
		this.downloadDirPath = downloadDirPath;
		this.proxyHost = proxyHost;
		this.proxyTcpPort = proxyTcpPort;
		initClient(shell);
	}
	
	private void initClient(Shell shell) {
		messages = new Config("messages");
		downloadDir = new File(downloadDirPath);
		this.shell = shell;
		this.shell.register(this);
		shellThread = new Thread(this.shell);
		shellThread.start();
		
		try {
			UnicastRemoteObject.exportObject((IRMICallback) this , 0);
		} catch (RemoteException e) {
			//export Error
		} 
		
		proxy = new Proxy(proxyHost, proxyTcpPort);
		
	}

	public ClientCli(Config config, Shell shell)
			throws MissingResourceException {

		downloadDirPath = config.getString("download.dir");
		proxyHost = config.getString("proxy.host");
		proxyTcpPort = config.getInt("proxy.tcp.port");
		
		initClient(shell);
				
	}
	

	
	@Command
	public MessageResponse rmitest() throws IOException{
		return proxy.rmitest();
	}
	
	@Command
	public MessageResponse subscribe(String filename, int numberOfDownloads) throws IOException{
		return proxy.subscribe(filename, numberOfDownloads, this);	
	}
	
	@Command
	public MessageResponse top3() throws IOException {
			return proxy.topThreeDownloads();
	}
	
	@Command
	public MessageResponse getProxyPublicKey() throws IOException {
		String fileContent = proxy.getProxyPublicKey().getMessage();
		
		if (!fileContent.equals(messages.getString("error.getProxyPublicKeyError")) && !fileContent.equals(messages.getString("error.noRMIConnection"))) {
		
			Date timestamp = new Date();
			//TODO: replace hardcoded path
			File file = new File("keys", timestamp.toString() + "_" + "proxy.pub.pem");
			file.createNewFile();
			
			BufferedWriter out = new BufferedWriter(new FileWriter(file));
			out.write(fileContent);
			out.close();
			
			
			return new MessageResponse("successfully received public key of Proxy");
		} else {
			return new MessageResponse(fileContent);
		}
		
	}
	
	@Command
	public MessageResponse setUserPublicKey(String username) throws IOException {
		//TODO: replace hardcoded path
		File clientKey = new File("keys", username + ".pub.pem");
		if (clientKey.exists()) {
			String fileAsString = "";
			try {
				fileAsString = ProxyManagementComponent.readFile(clientKey);
				return proxy.setUserPublicKey(username, fileAsString);
			} catch (IOException e) {
				//
			}
			
		}
		return new MessageResponse("Key for " + username + " does not exist");
			
	}
	
	
	
	
	
	
	
	@Command
	public MessageResponse readQuorum() throws IOException {
		if (proxy.readQuorum() != -1) {
			return new MessageResponse("ReadQuorum is set to " + proxy.readQuorum() + ".");
		}
		else return new MessageResponse(messages.getString("error.noRMIConnection"));
	}
	
	@Command
	public MessageResponse writeQuorum() throws IOException {
		if (proxy.writeQuorum() != -1) {
			return new MessageResponse("WriteQuorum is set to " + proxy.writeQuorum() + ".");
		}
		else return new MessageResponse(messages.getString("error.noRMIConnection"));
	}

	@Override
	@Command
	public LoginResponse login(String username, String password)
			throws IOException {
		LoginResponse response = proxy.login(new CryptLoginRequest(username,null, password));
		if (response.getType() == LoginResponse.Type.SUCCESS) {
			
			this.loggedInUserName = username;
		}
			
		return response;
	}

	@Override
	@Command
	public Response credits() throws IOException {
		
		return proxy.credits();
	}

	@Override
	@Command
	public Response buy(long credits) throws IOException {
		return proxy.buy(new BuyRequest(credits));
	}

	@Override
	@Command
	public Response list() throws IOException {
		return proxy.list();
	}

	@Override
	@Command
	public synchronized Response download(String filename) throws IOException {
		Response response = proxy.download(new DownloadTicketRequest(filename));
		
		if(response instanceof MessageResponse)
			return response;
		
		DownloadFileResponse fileResponse = (DownloadFileResponse) response;
		
		String fileContent = new String(fileResponse.getContent());
		
		File file = new File(downloadDir, fileResponse.getTicket().getFilename());
		
		file.createNewFile();
		
		BufferedWriter out = new BufferedWriter(new FileWriter(file));
		
		out.write(fileContent);
		out.close();
		
		return fileResponse;
	}

	@Override
	@Command
	public synchronized MessageResponse upload(String filename) throws IOException {
	
		File file = new File(downloadDir, filename);
		
		if(!file.exists())
			return new MessageResponse(messages.getString("error.fileDoesNotExist") + " ('" + filename + "')");
		
		BufferedReader reader = new BufferedReader(new FileReader(file));

		String line = null;
		StringBuilder stringBuilder = new StringBuilder();
		String ls = System.getProperty("line.separator");

		while ((line = reader.readLine()) != null) {
			stringBuilder.append(line);
			stringBuilder.append(ls);
		}
		reader.close();
		
		String content = stringBuilder.toString();
		
		return proxy.upload(new UploadRequest(filename, 0, content.getBytes()));
	}

	@Override
	@Command
	public MessageResponse logout() throws IOException {
		return proxy.logout();
	}

	@Override
	@Command
	public MessageResponse exit() throws IOException {

		proxy.logout();
		
		UnicastRemoteObject.unexportObject((IRMICallback) this, true);

		shellThread.interrupt();
		shell.close();
		System.in.close();
		proxy.cleanup();

		return new MessageResponse(messages.getString("info.shutdown"));
	}

	public static void main(String[] args) {
		try {
			Config config = new Config("client");
			Shell shell = new Shell("Client", System.out, System.in);

			new ClientCli(config, shell);
		} catch (MissingResourceException e) {
			System.out.println("Error: Client could not start, missing resources.");
		}
	}

	@Override
	public void notifySubscription(String filename, int numberOfDownloads) throws RemoteException {
		System.out.println("Notification: " + filename + " got downloaded " + numberOfDownloads + " times!");
		
	}

	@Override
	public String clientName() throws RemoteException {
		return this.loggedInUserName;
	}
}
